//
// Created by Administrator on 2025/7/22.
//

#include "fifo.h"

#include <string.h>
#include <stdbool.h>

void fifo_init(Fifo_t *ll, uint8_t *buffer, size_t size) {
    ll->head     = 0;
    ll->tail     = 0;
    ll->buffer   = buffer;
    ll->size     = size;
    ll->full     = 0;
    ll->overflow = 0;

    ll->pushLock = false;
    ll->pullLock = false;
}

void fifo_lock(Fifo_t *ll) {
    ll->pushLock = true;
    ll->pullLock = true;
}

void fifo_unlock(Fifo_t *ll) {
    ll->pushLock = false;
    ll->pullLock = false;
}

void fifo_clear(Fifo_t *ll) {
    fifo_lock(ll);

    ll->head     = 0;
    ll->tail     = 0;
    ll->full     = 0;
    ll->overflow = 0;

    memset(ll->buffer, 0, ll->size);

    fifo_unlock(ll);
}

size_t fifo_available(Fifo_t *ll) {
    if (ll->full) {
        return ll->size;
    }

    size_t free = (ll->size + ll->head - ll->tail) % ll->size;

    return free;
}

size_t fifo_push(Fifo_t *ll, uint8_t *data, size_t len) {
    if (ll->pushLock) {
        return 0;
    }

    if (data == NULL || len == 0 || ll->full) {
        return 0;
    }

    uint32_t left_size = 0;


    /* Calc available push size */
    uint32_t usable_len = ll->size - fifo_available(ll);
    len = len > usable_len ? usable_len : len;

    if (ll->head + len > ll->size - 1) {
        uint32_t cp_size = ll->size - ll->head;
        memcpy(&ll->buffer[ll->head], data, cp_size);
        ll->head = (ll->head + cp_size) % ll->size;

        data = data + cp_size;
        len  = len - cp_size;

        if (ll->head == ll->tail) {
            ll->full = true;
            ll->overflow++;
            return cp_size;
        }

        if (len == 0) {
            /* Data push over */
            return cp_size;
        }

        left_size = cp_size;
    }

    memcpy(&ll->buffer[ll->head], data, len);
    ll->head = (ll->head + len) % ll->size;

    if (ll->head == ll->tail) {
        ll->overflow++;
        ll->full = true;
    }

    return len + left_size;
}


size_t fifo_pull(Fifo_t *ll, uint8_t *data, size_t len) {
    if (ll->pullLock) {
        return 0;
    }

    if (data == NULL || len == 0) {
        return 0;
    }

    if (ll->head == ll->tail && !ll->full) {
        return 0;
    }

    uint32_t left_size = 0;

    uint32_t usable_len = fifo_available(ll);
    len = len > usable_len ? usable_len : len;

    if (ll->tail + len > ll->size - 1) {
        uint32_t cp_size = ll->size - ll->tail;
        memcpy(data, &ll->buffer[ll->tail], cp_size);
        ll->tail = (ll->tail + cp_size) % ll->size;

        len  = len - cp_size;
        data = data + cp_size;
        ll->full = false;

        if (ll->tail == ll->head) {
            return cp_size;
        }

        if (len == 0) {
            /* Data pull over */
            return cp_size;
        }

        left_size = cp_size;
    }

    memcpy(data, &ll->buffer[ll->tail], len);
    ll->tail = (ll->tail + len) % ll->size;

    ll->full = false;
    return len + left_size;
}

size_t fifo_read_into_keyword(Fifo_t *ll, char key, uint8_t *data, size_t len) {
    if (data == NULL || len == 0) {
        return 0;
    }

    if (ll->head == ll->tail && !ll->full) {
        return 0;
    }

    size_t p_read = ll->tail;

    uint32_t usable_len = fifo_available(ll);
    uint32_t tmp        = usable_len;

    uint8_t find = 0;
    while (tmp--) {
        if (ll->buffer[p_read] == (uint8_t) key) {
            find = 1;
            break;
        }
        p_read++;

        if (p_read > ll->size - 1) {
            p_read = 0;
        }
    }

    if (find == 0) {
        return 0;
    }

    uint32_t cp_size = usable_len - tmp;

    if (len < cp_size) {
        cp_size = len;
    }

    return fifo_pull(ll, data, cp_size);
}

size_t fifo_read_head_to_tail(Fifo_t *ll, char head, char tail, uint8_t *data, size_t len) {
    if (data == NULL || len == 0) {
        return 0;
    }

    size_t usable_len = fifo_available(ll);

    if (usable_len < 2) {
        return 0;
    }

    uint32_t tmp    = usable_len;
    size_t   p_read = ll->tail;

    uint8_t find_head = 0;
    while (tmp--) {
        if (ll->buffer[p_read] == (uint8_t) head) {
            find_head = 1;
            break;
        }

        p_read++;

        if (p_read > ll->size - 1) {
            p_read = 0;
        }
    }

    if (find_head == 0) {
        return 0;
    }

    /* move ll->tail to keyword ‘head’ */
    uint32_t lose_len = usable_len - tmp - 1;
    ll->tail = (ll->tail + lose_len) % ll->size;

    return fifo_read_into_keyword(ll, tail, data, len);
}

